package engine

import (
	"container/list"
	log "github.com/cihub/seelog"
	"github.com/shopspring/decimal"
	"matching/enum"
	"matching/jobline"
	"matching/middleware/cache"
	"matching/middleware/mq"
	"matching/model"
	"strconv"
	"time"
)

// Run 处理交易账本
func Run(symbol string, price decimal.Decimal,coinScale int32,baseCoinScale int32) {

	lastTradePrice := price
	book := &orderbook{}
	book.Init(symbol,coinScale,baseCoinScale)
	log.Infof("engine init success ,symbol is %s ,price is %s", symbol, lastTradePrice)

	//从通道中循环取订单
	for {
		order, ok := <-ChanMap[symbol]
		if !ok {
			//关闭该引擎通道
			log.Infof(" %s engine is closed", symbol)
			res:=cache.GetTime()
			end:=time.Now()
			start, _ := strconv.ParseInt(res, 10, 64)
			log.Infof("耗时:%d",end.Unix()-start)
			log.Info("撮合引擎结束......")
			book.PrintBookStatus()
			delete(ChanMap, symbol)
			//cache.ClearSymbol(symbol)
			return
		}
		//log.Infof("engine %s receive an order: %s", symbol, order.ToJson())
		switch order.Action {
		case enum.ActionCreate:
			doCreateOrder(&order, book, &lastTradePrice)
			break
		case enum.ActionCancel:
			//写入过来的订单应当是已构造好的结构体
			doCancelOrder(&order, book)
			break
		}
	}
}

// 创建订单
func doCreateOrder(order *model.Order, book *orderbook, lastTradePrice *decimal.Decimal) {
	switch order.Type {
	case enum.Limit:
		dealLimit(order, book, lastTradePrice)
		break
	case enum.LimitIoc:
		dealLimitIoc(order, book, lastTradePrice)
		break
	case enum.Market:
		dealMarket(order, book, lastTradePrice)
		break
	case enum.MarketTop5:
		dealMarketTop5(order, book, lastTradePrice)
		break
	case enum.MarketTop10:
		dealMarketTop10(order, book, lastTradePrice)
		break
	}
}

// 处理限价订单
func dealLimit(order *model.Order, book *orderbook, lastTradePrice *decimal.Decimal) {
	trades:=list.New()
	completeOrders :=list.New()
	//log.Infof("焦点单:%s",order.ToJson())
	/*
	市价单未成交部分撤单，如果不需要撤单，这部分代码可以开启
	cosM,traM:=matchMarketOrder(book, order, lastTradePrice)
	trades.PushBackList(traM)
	completeOrders.PushBackList(cosM)*/
	if !order.IsCompleted() {
		cosL,traL:=matchLimitOrder(book, order, lastTradePrice,0)
		trades.PushBackList(traL)
		completeOrders.PushBackList(cosL)
		if !order.IsCompleted() {
			//仍然无法匹配，放入订单队列
			//log.Infof("订单无法匹配完全，存入:%s",order.ToJson())
			book.addOrderBook(order)
			//log.Infof("存入成功")

		}
	}
	if trades.Len()>0 {
		// 成交记录集中推送
		mq.SendOrderTradeMsg(trades)
	}
	if completeOrders.Len()>0 {
		// 已完成的订单集中推送
		mq.SendOrderCompletedMsg(completeOrders)
		DeleteOrderSortFun(completeOrders)
	}
}

// 处理市价订单,市价单只跟限价单匹配
func dealMarket(order *model.Order, book *orderbook, lastTradePrice *decimal.Decimal) {
	trades:=list.New()
	completeOrders :=list.New()
	cosL,traL:=matchLimitOrder(book, order, lastTradePrice,0)
	trades.PushBackList(traL)
	completeOrders.PushBackList(cosL)
	if !order.IsCompleted() {
		//仍然无法匹配，撤单处理
		flag:=book.cancelOrderBook(order)
		//撤单回调推送
		mq.SendOrderCancelMsg(flag,order.OrderId)
	}
	if trades.Len()>0 {
		// 成交记录集中推送
		mq.SendOrderTradeMsg(trades)
	}
	if completeOrders.Len()>0 {
		// 已完成的订单集中推送
		mq.SendOrderCompletedMsg(completeOrders)
		DeleteOrderSortFun(completeOrders)
	}
}


// 限价成交剩余取消
func dealLimitIoc(order *model.Order, book *orderbook, lastTradePrice *decimal.Decimal) {
	trades:=list.New()
	completeOrders :=list.New()
	/*
	市价单未成交部分撤单，如果不需要撤单，这部分代码可以开启
	cosM,traM:=matchMarketOrder(book, order, lastTradePrice)
	trades.PushBackList(traM)
	completeOrders.PushBackList(cosM)*/
	if !order.IsCompleted() {
		cosL,traL:=matchLimitOrder(book, order, lastTradePrice,0)
		trades.PushBackList(traL)
		completeOrders.PushBackList(cosL)
		if !order.IsCompleted() {
			//仍然无法匹配，撤单处理
			flag:=book.cancelOrderBook(order)
			//撤单回调推送
			mq.SendOrderCancelMsg(flag,order.OrderId)
		}
	}
	if trades.Len()>0 {
		// 成交记录集中推送
		mq.SendOrderTradeMsg(trades)
	}
	if completeOrders.Len()>0 {
		// 已完成的订单集中推送
		mq.SendOrderCompletedMsg(completeOrders)
		DeleteOrderSortFun(completeOrders)
	}
}

// 市价成交最优5挡，超出取消
func dealMarketTop5(order *model.Order, book *orderbook, lastTradePrice *decimal.Decimal) {
	trades:=list.New()
	completeOrders :=list.New()
	if !order.IsCompleted() {
		cosL,traL:=matchLimitOrder(book, order, lastTradePrice,5)
		trades.PushBackList(traL)
		completeOrders.PushBackList(cosL)
		if !order.IsCompleted() {
			//仍然无法匹配，撤单处理
			flag:=book.cancelOrderBook(order)
			//撤单回调推送
			mq.SendOrderCancelMsg(flag,order.OrderId)
		}
	}
	if trades.Len()>0 {
		// 成交记录集中推送
		mq.SendOrderTradeMsg(trades)
	}
	if completeOrders.Len()>0 {
		// 已完成的订单集中推送
		mq.SendOrderCompletedMsg(completeOrders)
		DeleteOrderSortFun(completeOrders)
	}
}

// 市价成交最优10档，超出取消
func dealMarketTop10(order *model.Order, book *orderbook, lastTradePrice *decimal.Decimal) {
	trades:=list.New()
	completeOrders :=list.New()
	if !order.IsCompleted() {
		cosL,traL:=matchLimitOrder(book, order, lastTradePrice,10)
		trades.PushBackList(traL)
		completeOrders.PushBackList(cosL)
		if !order.IsCompleted() {
			//仍然无法匹配，撤单处理
			flag:=book.cancelOrderBook(order)
			// 撤单回调推送
			mq.SendOrderCancelMsg(flag,order.OrderId)
		}
	}
	if trades.Len()>0 {
		// 成交记录集中推送
		mq.SendOrderTradeMsg(trades)
	}
	if completeOrders.Len()>0 {
		// 已完成的订单集中推送
		mq.SendOrderCompletedMsg(completeOrders)
		DeleteOrderSortFun(completeOrders)
	}
}

func DeleteOrderSortFun(orders *list.List)  {
	for e := orders.Front(); e != nil; {
		order:=e.Value.(*model.Order)
		jobline.ChanDeleteOrderMap[order.Symbol]<-model.OrderSort{Symbol: order.Symbol, Action: enum.ActionCancel, OrderId: order.OrderId}
		jobline.ChanDeleteOrderMap[order.Symbol]<-model.OrderSort{Symbol: order.Symbol, Action: enum.ActionCreate, OrderId: order.OrderId}
	}
}


// 匹配限价订单逻辑,gear 进行匹配的档位
func matchLimitOrder(book *orderbook, focusOrder *model.Order, lastTradePrice *decimal.Decimal,gear int) (*list.List,*list.List) {
	tradeList:=list.New()
	completeOrder :=list.New()
	tryNum:=0
LOOP:
	{
		//从卖单头部取出订单进行匹配，未成交完成继续匹配
		headerOrder := book.getLimitHeaderBySide(focusOrder.Side)
		if headerOrder != nil  {
			if (focusOrder.IsMarket() || (focusOrder.Side==enum.SideBuy &&headerOrder.Price.LessThanOrEqual(focusOrder.Price))) ||(focusOrder.Side==enum.SideSell &&headerOrder.Price.GreaterThanOrEqual(focusOrder.Price)) {
				trade,flag:=processMatch(book,focusOrder, headerOrder, lastTradePrice)
				if flag {
					tradeList.PushBack(trade)
				}
				if headerOrder.IsCompleted() ||book.OrderFoundCompleted(headerOrder,trade.Price) {
					//匹配单已完成匹配，移除
					completeOrder.PushFront(headerOrder)
					book.removeOrder(headerOrder)
				}
				tryNum++

				//订单未完成并且剩余部分可继续成交
				if !focusOrder.IsCompleted() &&  !book.OrderFoundCompleted(focusOrder,trade.Price) {
					if gear==0 || tryNum< gear {
						//log.Infof("未成交完成订单详情:%s,对手单详情：%s,成交价:%s",focusOrder.ToJson(),headerOrder.ToJson(),trade.Price)
						goto LOOP
					}
				} else {
					//焦点单已完成
					completeOrder.PushFront(focusOrder)
				}
			}else {
				return completeOrder,tradeList
			}

		}
	}
	return completeOrder,tradeList
}

// 匹配市价订单逻辑,市价跟市价无法成交，因此对市价的撮合不需要考虑档位问题
func matchMarketOrder(book *orderbook, focusOrder *model.Order, lastTradePrice *decimal.Decimal) (*list.List,*list.List){
	completeOrder :=list.New()
	tradeList:=list.New()
LOOP:
	{
		//从卖单头部取出订单进行匹配，未成交完成继续匹配
		headerOrder := book.getMarketHeaderBySide(focusOrder.Side)
		if headerOrder != nil && headerOrder.Price.LessThanOrEqual(focusOrder.Price) {
			trade,flag:=processMatch(book,focusOrder, headerOrder, lastTradePrice)
			if flag {
				tradeList.PushBack(trade)
			}
			if headerOrder.IsCompleted() {
				completeOrder.PushFront(headerOrder)
				//匹配单已完成匹配，移除
				book.removeOrder(headerOrder)
			}
			if !focusOrder.IsCompleted() {
				goto LOOP
			}else {
				//焦点单已完成
				completeOrder.PushFront(headerOrder)
			}
		}
	}
	return completeOrder,tradeList
}

func processMatch(book *orderbook, focusOrder *model.Order, matchOrder *model.Order, lastTradePrice *decimal.Decimal) (model.ExchangeTrade,bool) {

	var tradePrice decimal.Decimal
	var buyerOrderId string
	var sellerOrderId string
	var buyerId string
	var sellerId string
	if matchOrder.Side == enum.SideBuy {
		tradePrice = chooseTradePrice(matchOrder, focusOrder, lastTradePrice)
		buyerOrderId = matchOrder.OrderId
		sellerOrderId = focusOrder.OrderId
		buyerId = matchOrder.UserId
		sellerId = focusOrder.UserId
	} else {
		tradePrice = chooseTradePrice(matchOrder, focusOrder, lastTradePrice)
		buyerOrderId = focusOrder.OrderId
		sellerOrderId = matchOrder.OrderId
		buyerId = focusOrder.UserId
		sellerId = matchOrder.UserId
	}
	// 确定成交量
	tradeAmount := chooseTradeAmount(tradePrice, focusOrder, matchOrder,book)
	if tradeAmount.Cmp(decimal.Zero) <=0 {
		return model.ExchangeTrade{Price :tradePrice},false
	}
	// 成交额
	turnover := tradeAmount.Mul(tradePrice)

	focusOrder.TradedAmount = focusOrder.TradedAmount.Add(tradeAmount)
	matchOrder.TradedAmount = matchOrder.TradedAmount.Add(tradeAmount)
	focusOrder.Turnover = focusOrder.Turnover.Add(turnover)
	matchOrder.Turnover = matchOrder.Turnover.Add(turnover)

	trade := model.ExchangeTrade{
		Symbol:      focusOrder.Symbol,
		Price:       tradePrice,
		Amount:      tradeAmount,
		Turnover:    turnover,
		Side:        focusOrder.Side,
		BuyOrderId:  buyerOrderId,
		SellOrderId: sellerOrderId,
		Timestamp:   time.Now().UnixNano(),
		BuyerId:     buyerId,
		SellerId:    sellerId,
	}
	*lastTradePrice=tradePrice
	book.SaveTradeChangesBatch(focusOrder,matchOrder,tradePrice)
	return trade,true
}





// 计算可成交量，这里要区分市价单和限价单
func chooseTradeAmount(tradePrice decimal.Decimal, focusOrder *model.Order, matchOrder *model.Order,book *orderbook) decimal.Decimal {
	// 取两个订单中，成交量最小的
	var focAmount decimal.Decimal
	var matAmount decimal.Decimal
	if focusOrder.IsMarket()&&focusOrder.Side==enum.SideBuy {
		//市价单
		focAmount = (focusOrder.Amount.Sub(focusOrder.Turnover)).DivRound(tradePrice, book.coinScale)
	} else {
		focAmount = focusOrder.Amount.Sub(focusOrder.TradedAmount)
	}

	if matchOrder.IsMarket()&&matchOrder.Side==enum.SideBuy  {
		matAmount = (matchOrder.Amount.Sub(matchOrder.Turnover)).DivRound(tradePrice, book.coinScale)
	} else {
		matAmount = matchOrder.Amount.Sub(matchOrder.TradedAmount)
	}
	if focAmount.Cmp(matAmount) >= 0 {
		return matAmount
	} else {
		return focAmount
	}
}

// 计算成交价
func chooseTradePrice(buyOrder *model.Order, sellOrder *model.Order, lastPrice *decimal.Decimal) decimal.Decimal {
	// 不存在两个市价单互相匹配，此处不做控制
	// 买入价必须大于或等于卖出价才能撮合成交。 当买入价等于卖出价时，成交价就是买入价或卖出价。
	//当买入价大于卖出价时，则还要参考前一笔成交价来确定最新成交价。假设买入价为 B，卖出价为 S，前一笔成交价为 P，最新成交价为 N，那么：
	//如果 P >= B，则 N = B
	//如果 P <= S，则 N = S
	//如果 B > P > S，则 N = P
	if buyOrder.IsMarket() {
		return sellOrder.Price
	}
	if sellOrder.IsMarket() {
		return buyOrder.Price
	}
	if buyOrder.Price.Equal(sellOrder.Price) {
		return buyOrder.Price
	} else {
		if lastPrice.GreaterThanOrEqual(buyOrder.Price) {
			return buyOrder.Price
		} else {
			return sellOrder.Price
		}
	}
}




// 撤单
func doCancelOrder(order *model.Order,book *orderbook) {
	flag:=book.cancelOrderBook(order)
	//撤单回调推送
	mq.SendOrderCancelMsg(flag,order.OrderId)
	//撤单成功后，应删除撤单定序集合、下单定序集合中的订单
	if flag {
		jobline.ChanDeleteOrderMap[order.Symbol]<-model.OrderSort{Symbol: order.Symbol, Action: enum.ActionCancel, OrderId: order.OrderId}
		jobline.ChanDeleteOrderMap[order.Symbol]<-model.OrderSort{Symbol: order.Symbol, Action: enum.ActionCreate, OrderId: order.OrderId}
	}
}


